package ferp.core.mode;

import ferp.core.ai.tree2.Evaluator;
import ferp.core.ai.tree2.Score;
import ferp.core.game.Game;
import ferp.core.game.Input;
import ferp.core.game.Settings;
import ferp.core.player.Player;

/**
 * User: olegs
 * Date: 19/05/11 15:11
 */
public abstract class TrickingBase extends Moving
{
  @Override
  public int evaluator()
  {
    return Evaluator.TRICKING;
  }

  @Override
  public void score(Game g, Settings settings)
  {
    // reset consequent passing sets counter if the game was actually played
    // or if it is configured to interrupt passing for failed games
    if (g.getDeclarerTricks() >= g.getDeclarerBid().current.tricks || settings.isPassingInterruptedByFailedGame())
      g.consequentPassingSets = 0;
  }

  @Override
  protected Act act(Game.Listener listener, Game g, Settings settings, Input input) throws Game.Error
  {
    switch (input.offer)
    {
      case NO_MORE_TRICKS:
        // this is for declarer only!
        int p2 = Game.previous(g.player.declarer);
        int p3 = Game.next(g.player.declarer);

        // assign remaining tricks to the other players
        g.addTricks(p2, Score.get((short)input.score, p2));
        g.addTricks(p3, Score.get((short)input.score, p3));

        // if the declarer made a mistake and there are tricks left distribute them evenly between the opponents
        // and the reminder should go to the loosing player
        int error = Score.get((short)input.score, g.player.declarer);

        if (error > 0)
        {
          if (g.whisters == 2 || g.passers == 2)
          {
            int looser = g.getScore(p2).balance2.current > g.getScore(p3).balance2.current ? p3 : p2;
            int reminder = error % 2;
            int half = error / 2;

            g.addTricks(p2, half);
            g.addTricks(p3, half);
            g.addTricks(looser, reminder);
          }
          else
            g.addTricks(g.opponent().id(), error);
        }

        return Act.DONE;

      case REST_TRICKS_ARE_MINE:
        // score is relative => number of rounds to play has to be equal or greater than score
        int rest = Game.MAXIMUM_ROUNDS - g.rounds;

        if (rest == Score.get((short)input.score, g.player.current))
        {
          g.addTricks(g.player.current, rest);

          return Act.DONE;
        }

        listener.onOfferDeclined();

        return Act.STAY;

      case NO_OFFER:
        return super.act(listener, g, settings, input);

      default:
        // get offered tricks value
        int offer = g.getDeclarerBid().current.tricks + (input.offer.ordinal() - Input.Offer.TRICKS_BY_CONTRACT.ordinal());
        int score = Score.get((short)input.score, g.player.declarer);

        if ((g.declarer() == g.human() && g.getDeclarerTricks() + score >= offer) ||
            (g.declarer() != g.human() && g.getDeclarerTricks() + score <= offer))
        {
          g.setTricks(g.player.declarer, offer);

          // this can be either whisted contract case (open type game only)
          // or a checked play of a game of 10
          if (g.whisters == 1)
            g.setTricks(g.opponent().id(), 10 - g.getDeclarerTricks() - g.getTricks(g.passer().id()));
          else
            g.addTricks(Game.next(g.declarer().id()), 10 - offer - g.getTricks(Game.previous(g.declarer().id())));

          return Act.DONE;
        }

        listener.onOfferDeclined();

        return Act.STAY;
    }
  }

  @Override
  protected void setMoverInputOptions(Game.Listener listener, Game g, Player mover) throws Game.Error
  {
    mover.setTrickingInputOptions(listener, g);
  }
}
